package com.biz.primus.ms.tms.service;

import com.biz.primus.base.exception.BizBusinessException;
import com.biz.primus.base.exception.utils.AssertUtils;
import com.biz.primus.common.utils.DateUtil;
import com.biz.primus.model.depot.vo.EmployeeVo;
import com.biz.primus.model.tms.enums.DeliveryOrderStatus;
import com.biz.primus.model.tms.enums.SignType;
import com.biz.primus.model.tms.enums.TakeDeliveryOrderEnum;
import com.biz.primus.model.tms.exception.TmsExceptions;
import com.biz.primus.model.tms.vo.*;
import com.biz.primus.ms.base.service.AbstractBaseService;
import com.biz.primus.ms.tms.dao.po.DeliveryOrderPo;
import com.biz.primus.ms.tms.dao.po.DeliverySignItemPo;
import com.biz.primus.ms.tms.dao.po.TakeDeliverOrderLog;
import com.biz.primus.ms.tms.dao.repository.DeliverOrderRepository;
import com.biz.primus.ms.tms.dao.repository.TakeDeliverOrderLogRepository;
import com.biz.primus.ms.tms.feign.EmployeeFeignClient;
import com.biz.primus.ms.tms.feign.OmsOrderFeignClient;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.sql.Timestamp;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * LogisticsService
 *
 * @author JKLiues
 * @date 2017/11/22
 */
@Service
@Slf4j
public class LogisticsService extends AbstractBaseService {


    private final DeliverOrderRepository deliverOrderRepository;
    private final OmsOrderFeignClient omsOrderFeignClient;
    private final EmployeeFeignClient employeeFeignClient;
    private final TakeDeliverOrderLogRepository logRepository;

    @Autowired
    public LogisticsService(DeliverOrderRepository deliverOrderRepository, OmsOrderFeignClient omsOrderFeignClient, EmployeeFeignClient employeeFeignClient, TakeDeliverOrderLogRepository logRepository) {
        this.deliverOrderRepository = deliverOrderRepository;
        this.omsOrderFeignClient = omsOrderFeignClient;
        this.employeeFeignClient = employeeFeignClient;
        this.logRepository = logRepository;
    }

    /**
     * 接单
     *
     * @param reqVo 接单请求VO
     */
    @Transactional(rollbackFor = Exception.class)
    public void takeDeliverOrder(TmsLogisticsBaseReqVo reqVo) {
        String deliveryCode = reqVo.getDeliveryCode();
        AssertUtils.hasLength(deliveryCode, TmsExceptions.DELIVER_ORDER_NOT_EXIST);
        DeliveryOrderPo deliveryOrder = deliverOrderRepository.findByDeliveryCode(deliveryCode);
        AssertUtils.notNull(deliveryOrder, TmsExceptions.DELIVER_ORDER_NOT_EXIST);
        EmployeeVo employee = employeeFeignClient.findByEmployeeId(reqVo.getUserId());
        String depotCode = employee.getDepotCode();
        String empCode = employee.getEmployeeCode();
        AssertUtils.isTrue(Objects.equals(depotCode, deliveryOrder.getDepotCode()), TmsExceptions.NOT_ALLOWED_TAKE_DELIVER_ORDER);
        AssertUtils.isTrue(deliveryOrder.getDeliveryStatus() == DeliveryOrderStatus.WAIT_BE_SINGLE, TmsExceptions.NOT_ALLOWED_TAKE_DELIVER_ORDER);
        deliveryOrder.setDeliveryUserNo(empCode);
        deliveryOrder.setDeliveryUserName(employee.getName());
        deliveryOrder.setDeliveryUserMobile(employee.getMobile());
        deliveryOrder.setOrderTakeTime(DateUtil.now());
        deliveryOrder.setDeliveryStatus(DeliveryOrderStatus.DELIVERING);
        deliverOrderRepository.save(deliveryOrder);
        delayer.executeAfterTransactionCommit(() -> {
            TakeDeliverOrderLog log = new TakeDeliverOrderLog();
            log.setId(idService.getNextId());
            log.setDeliveryOrderCode(deliveryCode);
            log.setEmpCode(empCode);
            log.setType(TakeDeliveryOrderEnum.TAKE);
            logRepository.save(log);
        });
    }

    /**
     * 取消接单
     *
     * @param reqVo 取消接单请求
     */
    @Transactional(rollbackFor = Exception.class)
    public void cancelDelivery(TmsLogisticsBaseReqVo reqVo) {
        String deliveryCode = reqVo.getDeliveryCode();
        String empCode = employeeFeignClient.getCodeById(reqVo.getUserId());
        AssertUtils.hasLength(deliveryCode, TmsExceptions.DELIVER_ORDER_NOT_EXIST);
        DeliveryOrderPo deliveryOrder = deliverOrderRepository.findByDeliveryCode(deliveryCode);
        AssertUtils.notNull(deliveryOrder, TmsExceptions.DELIVER_ORDER_NOT_EXIST);
        String deliveryUserNo = deliveryOrder.getDeliveryUserNo();
        AssertUtils.isTrue(Objects.equals(empCode, deliveryUserNo), TmsExceptions.OPERATOR_NOT_ALLOWED);
        deliveryOrder.init();
        deliverOrderRepository.save(deliveryOrder);
        delayer.executeAfterTransactionCommit(() -> {
            TakeDeliverOrderLog log = new TakeDeliverOrderLog();
            log.setId(idService.getNextId());
            log.setDeliveryOrderCode(deliveryCode);
            log.setEmpCode(empCode);
            log.setType(TakeDeliveryOrderEnum.CANCEL);
            logRepository.save(log);
        });
    }

    /**
     * 开始配送
     *
     * @param reqVo 配送请求
     */
    public void distribution(TmsLogisticsBaseReqVo reqVo) {
        String deliveryCode = reqVo.getDeliveryCode();
        Long userId = reqVo.getUserId();
        String empCode = employeeFeignClient.getCodeById(userId);
        AssertUtils.hasLength(deliveryCode, TmsExceptions.DELIVER_ORDER_CODE_IS_NULL);
        DeliveryOrderPo deliverOrder = deliverOrderRepository.findByDeliveryCode(deliveryCode);
        AssertUtils.notNull(deliverOrder, TmsExceptions.DELIVER_ORDER_NOT_EXIST);
        String deliveryUserNo = deliverOrder.getDeliveryUserNo();
        AssertUtils.isTrue(Objects.equals(empCode, deliveryUserNo), TmsExceptions.OPERATOR_NOT_ALLOWED);
        DeliveryOrderStatus deliveryStatus = deliverOrder.getDeliveryStatus();
        AssertUtils.isTrue(deliveryStatus == DeliveryOrderStatus.DELIVERING, TmsExceptions.DELIVER_ORDER_STATUS_ERROR);
        log.info("开始处理 配送员[{}]开始配送[{}]", empCode, deliveryCode);
        deliverOrder.setStartLon(reqVo.getLongitude());
        deliverOrder.setStartLat(reqVo.getLatitude());
        deliverOrder.setStartTime(DateUtil.now());
        deliverOrderRepository.save(deliverOrder);
    }

    /**
     * 确认到达
     *
     * @param reqVo 请求Vo
     */
    public void arriveDestination(TmsLogisticsBaseReqVo reqVo) {
        Long userId = reqVo.getUserId();
        String deliveryCode = reqVo.getDeliveryCode();
        AssertUtils.hasLength(deliveryCode, TmsExceptions.DELIVER_ORDER_CODE_IS_NULL);
        DeliveryOrderPo deliverOrder = deliverOrderRepository.findByDeliveryCode(deliveryCode);
        AssertUtils.notNull(deliverOrder, TmsExceptions.DELIVER_ORDER_NOT_EXIST);
        String userNo = deliverOrder.getDeliveryUserNo();
        String empCode = employeeFeignClient.getCodeById(userId);
        AssertUtils.isTrue(Objects.equals(empCode, userNo), TmsExceptions.OPERATOR_NOT_ALLOWED);
        DeliveryOrderStatus deliveryStatus = deliverOrder.getDeliveryStatus();
        AssertUtils.isTrue(deliveryStatus == DeliveryOrderStatus.DELIVERING, TmsExceptions.DELIVER_ORDER_STATUS_ERROR);
        log.info("开始处理配送单[{}]确认到达", deliverOrder.getDeliveryCode());
        final Timestamp nowTimestamp = DateUtil.now();
        deliverOrder.setArriveTime(nowTimestamp);
        deliverOrder.setArriveLon(reqVo.getLongitude());
        deliverOrder.setArriveLat(reqVo.getLatitude());
        deliverOrder.setDeliveryStatus(DeliveryOrderStatus.ARRIVED);
        deliverOrderRepository.save(deliverOrder);
    }

    /**
     * 完成订单（签收）
     *
     * @param reqVo 完成订单请求
     */
    public void finishOrder(FinishOrderReqVo reqVo) {
        Long userId = reqVo.getUserId();
        String deliveryCode = reqVo.getDeliveryCode();
        AssertUtils.hasLength(deliveryCode, TmsExceptions.DELIVER_ORDER_CODE_IS_NULL);
        DeliveryOrderPo deliverOrder = deliverOrderRepository.findByDeliveryCode(deliveryCode);
        AssertUtils.notNull(deliverOrder, TmsExceptions.DELIVER_ORDER_NOT_EXIST);
        String userNo = deliverOrder.getDeliveryUserNo();
        String empCode = employeeFeignClient.getCodeById(userId);
        AssertUtils.isTrue(Objects.equals(empCode, userNo), TmsExceptions.OPERATOR_NOT_ALLOWED);
        DeliveryOrderPo deliveryOrder = deliverOrderRepository.findByDeliveryCode(deliveryCode);
        DeliveryOrderStatus deliveryStatus = deliverOrder.getDeliveryStatus();
        AssertUtils.isTrue(deliveryStatus == DeliveryOrderStatus.ARRIVED, TmsExceptions.DELIVER_ORDER_STATUS_ERROR);
        List<SimpleOrderInfoRespVo> orderInfo = omsOrderFeignClient.findOrderInfo(Lists.newArrayList(deliverOrder.getOrderCode()));
        AssertUtils.notEmpty(orderInfo, TmsExceptions.ORDER_NOT_FOUNT);
        SimpleOrderInfoRespVo orderInfoRespVo = orderInfo.get(0);
        log.info("开始处理配送单[{}]签收", reqVo.getDeliveryCode());
        SignType signType = reqVo.getSignType();
        deliveryOrder.setRemark(reqVo.getRemark());
        List<DeliverySignItemPo> signItems;
        switch (signType) {
            case All:
                deliveryOrder.setCompleteLat(reqVo.getLatitude());
                deliveryOrder.setCompleteLon(reqVo.getLongitude());
                deliveryOrder.setCompleteTime(DateUtil.now());
                deliveryOrder.setDeliveryStatus(DeliveryOrderStatus.FINISH);
                signItems = this.buildSignAllItem(deliveryOrder, orderInfoRespVo);
                break;
            case REJECT_SIGNED:
                deliveryOrder.setCompleteLat(reqVo.getLatitude());
                deliveryOrder.setCompleteLon(reqVo.getLongitude());
                deliveryOrder.setCompleteTime(DateUtil.now());
                deliveryOrder.setDeliveryStatus(DeliveryOrderStatus.FINISH);
                signItems = null;
                break;
            case EXTENSION:
                deliveryOrder.setDeliveryStatus(DeliveryOrderStatus.DELAY_SIGNED);
                Timestamp delayTime = reqVo.getDelayTime();
                AssertUtils.notNull(delayTime, TmsExceptions.DELIVER_ORDER_DELAY_TIME_IS_NULL);
                deliverOrder.setDelayDeliveryTime(delayTime);
                signItems = null;
                break;
            case SECTION:
                deliveryOrder.setCompleteLat(reqVo.getLatitude());
                deliveryOrder.setCompleteLon(reqVo.getLongitude());
                deliveryOrder.setCompleteTime(DateUtil.now());
                deliveryOrder.setDeliveryStatus(DeliveryOrderStatus.FINISH);
                signItems = this.buildSectionItem(deliverOrder, reqVo, orderInfoRespVo);
                break;
            default:
                throw new BizBusinessException("不支持的签收状态");
        }
        deliveryOrder.setDeliveryPartSignItems(signItems);
    }

    /**
     * 部分签收-签收商品项构建方法
     */
    private List<DeliverySignItemPo> buildSectionItem(DeliveryOrderPo deliverOrder, FinishOrderReqVo reqVo, SimpleOrderInfoRespVo orderInfoRespVo) {
        List<OrderSignItemReqVo> signItem = reqVo.getSignItem();
        AssertUtils.notEmpty(signItem, TmsExceptions.DELIVER_ORDER_ITEM_IS_EMPTY);
        List<OrderInfoItemRespVo> productItems = orderInfoRespVo.getProductItems();
        AssertUtils.notEmpty(productItems, TmsExceptions.DELIVER_ORDER_ITEM_IS_EMPTY);
        Map<String, Integer> map = productItems.stream()
                .collect(Collectors.toMap(OrderInfoItemRespVo::getProductCode, OrderInfoItemRespVo::getQuantity));
        return signItem.stream().map(vo -> {
            String productCode = vo.getProductCode();
            Integer quantity = map.get(productCode);
            Integer signQuantity = vo.getSignQuantity();
            AssertUtils.notNull(quantity, TmsExceptions.DELIVER_ORDER_SIGN_ITEM_NOT_IN_ORDER);
            AssertUtils.isTrue(signQuantity >= 0 && quantity >= signQuantity, TmsExceptions.DELIVER_ORDER_SIGN_QUANTITY_GT_QUANTITY_IN_ORDER);
            DeliverySignItemPo po = new DeliverySignItemPo();
            po.setDeliveryOrder(deliverOrder);
            po.setProductCode(productCode);
            po.setSignQuantity(signQuantity);
            po.setPurchaseQuantity(quantity);
            po.setId(idService.getNextId());
            return po;
        }).collect(Collectors.toList());
    }

    /**
     * 全部签收-签收商品项构建方法
     */
    private List<DeliverySignItemPo> buildSignAllItem(DeliveryOrderPo deliveryOrder, SimpleOrderInfoRespVo orderInfoRespVo) {
        List<OrderInfoItemRespVo> productItems = orderInfoRespVo.getProductItems();
        AssertUtils.notEmpty(productItems, TmsExceptions.DELIVER_ORDER_ITEM_IS_EMPTY);
        return productItems.stream().map(vo -> {
            DeliverySignItemPo po = new DeliverySignItemPo();
            po.setDeliveryOrder(deliveryOrder);
            po.setProductCode(vo.getProductCode());
            po.setSignQuantity(vo.getQuantity());
            po.setPurchaseQuantity(vo.getQuantity());
            po.setId(idService.getNextId());
            return po;
        }).collect(Collectors.toList());
    }
}
